/*
 * This file is part of the Try! Engine project.
 *
 * Copyright 2010-2011 Emanuele Bertoldi. All rights reserved.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * You should have received a copy of the modified BSD License along with this
 * program. If not, see <http://www.opensource.org/licenses/bsd-license.php>
 */

#include "model3d.h"
#include "graphicsmanager.h"

using namespace Try;

PropertyName Model3D::name  = "try.graphics.model3d";

Model3D::Model3D(Object* owner, Model3DNode* model3d)
    : SceneTransformable(Model3D::name, owner, model3d)
{
    graphics->addListener(this);

    Property::registerDeserialize(Model3D::name, &Model3D::deserializeThis);
}

Model3D::Model3D(
        Object* owner,
        const String& filename,
        const Transform& t,
        SceneNode* parent
        )
    : SceneTransformable(Model3D::name, owner, new Model3DNode(filename, t, parent))
{
    graphics->addListener(this);

    Property::registerDeserialize(Model3D::name, &Model3D::deserializeThis);
}

Model3D::~Model3D()
{
    graphics->removeListener(this);

    this->stopAllAnimations();
}

int Model3D::addAnimation(const String& filename, float weight, int layer)
{
    Model3DNode* node = this->sceneNode<Model3DNode>();

    if (node)
        return node->addAnimation(filename, weight, layer);

    return -1;
}

void Model3D::removeAllAnimations()
{
    Model3DNode* node = this->sceneNode<Model3DNode>();

    if (node)
        node->removeAllAnimations();
}

void Model3D::removeAnimation(int animation_id)
{
    Model3DNode* node = this->sceneNode<Model3DNode>();

    if (node)
        node->removeAnimation(animation_id);
}

void Model3D::runAllAnimations(float weight, float speed)
{
    Model3DNode* node = this->sceneNode<Model3DNode>();

    if (node)
    {
        for (int i=0; i<node->animationCount(); i++)
            this->runAnimation(i, weight, speed);
    }
}

void Model3D::runAnimation(int animation_id, float weight, float speed)
{
    Model3DNode* node = this->sceneNode<Model3DNode>();

    if (node)
    {
        node->changeAnimationWeight(animation_id, weight);
        m_runningAnimations[animation_id] = speed;
    }
}

void Model3D::pauseAllAnimations()
{
    Model3DNode* node = this->sceneNode<Model3DNode>();

    if (node)
    {
        for (int i=node->animationCount(); i>0; i--)
            this->pauseAnimation(i-1);
    }
}

void Model3D::pauseAnimation(int animation_id)
{
    Model3DNode* node = this->sceneNode<Model3DNode>();

    if (node)
    {
        m_runningAnimations.erase(animation_id);
        node->changeAnimationWeight(animation_id, 0);
    }
}

void Model3D::stopAllAnimations()
{
    Model3DNode* node = this->sceneNode<Model3DNode>();

    if (node)
    {
        for (int i=node->animationCount(); i>0; i--)
            this->stopAnimation(i-1);
    }
}

void Model3D::stopAnimation(int animation_id)
{
    Model3DNode* node = this->sceneNode<Model3DNode>();

    if (node)
    {
        this->pauseAnimation(animation_id);

        node->resetAnimation(animation_id);
    }
}

void Model3D::changeAnimationWeight(int animation_id, float weight)
{
    Model3DNode* node = this->sceneNode<Model3DNode>();

    if (node)
        node->changeAnimationWeight(animation_id, weight);
}

int Model3D::animationCount() const
{
    Model3DNode* node = this->sceneNode<Model3DNode>();

    if (node)
        return node->animationCount();

    return 0;
}

bool Model3D::isAnimationRunning(int animation_id) const
{
    if (m_runningAnimations.find(animation_id) == m_runningAnimations.end())
        return false;

    return true;
}

float Model3D::animationTime(int animation_id) const
{
    Model3DNode* node = this->sceneNode<Model3DNode>();

    if (node)
        return node->animationTime(animation_id);

    return 0;
}

float Model3D::animationWeight(int animation_id) const
{
    Model3DNode* node = this->sceneNode<Model3DNode>();

    if (node)
        return node->animationWeight(animation_id);

    return 0;
}

Property* Model3D::copy(Object* owner) const
{
    return 0;
}

String Model3D::serialize() const
{
    return "";
}

void Model3D::onRenderStarted(double dt)
{
    Model3DNode* node = this->sceneNode<Model3DNode>();

    if (node)
    {
        std::map<int, float>::iterator it = m_runningAnimations.begin();

        while (it != m_runningAnimations.end())
        {
            node->updateAnimation((*it).first, dt * (*it).second);
            it++;
        }
    }
}

Property* Model3D::deserializeThis(XmlNode* node, Object* owner)
{
    Property* result = 0;

    if (node && String(node->name()) == "property")
    {
        String name = node->first_attribute("name")->value();

        if (name == Model3D::name)
        {
            String filename = node->first_attribute("filename")->value();

            Transform transform;
            SceneNode* parent = SceneNode::rootNode();

            result = new Model3D(owner, filename, transform, parent);
        }
    }

    return result;
}
